package com.xuecheng.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.xuecheng.commons.enums.ErrorCode;
import com.xuecheng.commons.model.dto.CoursePubIndexDto;
import com.xuecheng.commons.model.dto.PageRequest;
import com.xuecheng.commons.model.vo.CoursePubIndexVo;
import com.xuecheng.commons.model.vo.CoursePubVo;
import com.xuecheng.commons.model.vo.PageResponseResult;
import com.xuecheng.commons.model.vo.ResponseResult;
import com.xuecheng.commons.utils.BeanHelper;
import com.xuecheng.content.api.ContentClient;
import com.xuecheng.search.domain.CoursePubIndex;
import com.xuecheng.search.service.SearchService;
import com.xuecheng.web.exceptions.BusinessException;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private ContentClient contentClient;

    /**
     * 搜索课程
     *
     * @param coursePubIndexDto 搜索条件
     * @param pageRequest       分页数据
     * @return 结果
     */
    @Override
    public PageResponseResult findCourseByEs(CoursePubIndexDto coursePubIndexDto, PageRequest pageRequest) throws IOException {
        //创建查询请求
        SearchRequest searchRequest = new SearchRequest("course");
        //创建复合查询请求
        BoolQueryBuilder boolQuery = new BoolQueryBuilder();
        //设置条件
        //分页
        searchRequest.source().from((pageRequest.getPageNo() - 1) * pageRequest.getPageSize())
                .size(pageRequest.getPageSize());
        //关键字
        if (StringUtils.isEmpty(coursePubIndexDto.getKeyword())) {
            //没有搜索关键字直接查询所有
            boolQuery.must(QueryBuilders.matchAllQuery());
        } else {
            //有关键字就去all字段查询
            boolQuery.must(QueryBuilders.matchQuery("all", coursePubIndexDto.getKeyword()));
        }
        //判断分类
        if (StringUtils.isNotEmpty(coursePubIndexDto.getMt())) {
            boolQuery.filter(QueryBuilders.termQuery("mt", coursePubIndexDto.getMt()));
        }
        if (StringUtils.isNotEmpty(coursePubIndexDto.getSt())) {
            boolQuery.filter(QueryBuilders.termQuery("st", coursePubIndexDto.getSt()));
        }
        //判断等级
        if (StringUtils.isNotEmpty(coursePubIndexDto.getGrade())) {
            boolQuery.filter(QueryBuilders.termQuery("grade", coursePubIndexDto.getGrade()));
        }
        //设置高亮
        searchRequest.source().highlighter(new HighlightBuilder().field("name").preTags("<font color='red'>").postTags("</font>").requireFieldMatch(false));
        searchRequest.source().query(boolQuery);
        //发送请求
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        //解析响应并返回
        return parseTheResponse(response);
    }

    /**
     * 同步Es索引库
     *
     * @param coursePubId 课程发布id
     */
    @Override
    public void cogradientIndex(Long coursePubId) throws BusinessException, IOException {
        //远程调用查询
        ResponseResult<CoursePubVo> responseResult = contentClient.findById(coursePubId);
        //判断响应的Code
        if (ErrorCode.SUCCESS.getCode() != responseResult.getCode()) {
            throw new BusinessException(ErrorCode.PARAMSERROR);
        }
        CoursePubVo coursePubVo = responseResult.getData();
        //转成Index实体类
        CoursePubIndex coursePubIndex = BeanHelper.copyProperties(coursePubVo, CoursePubIndex.class);
        coursePubIndex.setCourse_id(coursePubVo.getCourseId());
        coursePubIndex.setCompany_id(coursePubVo.getCompanyId());
        coursePubIndex.setCompany_name(coursePubVo.getCompanyName());
        coursePubIndex.setIs_pub(coursePubVo.getIsPub());
        coursePubIndex.setIs_latest(coursePubVo.getIsLatest());
        coursePubIndex.setCreate_date(coursePubVo.getCreateDate());
        coursePubIndex.setChange_date(coursePubVo.getChangeDate());
        //创建Index请求
        IndexRequest indexRequest = new IndexRequest("course");
        //设置id
        indexRequest.id(coursePubIndex.getId().toString());
        //构造请求条件
        indexRequest.source(JSON.toJSONString(coursePubIndex), XContentType.JSON);
        //发送请求
        restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
    }


    /**
     * 解析响应
     *
     * @param response 响应内容
     * @return 结果
     */
    public PageResponseResult parseTheResponse(SearchResponse response) {
        //解析响应
        SearchHits hits = response.getHits();
        SearchHit[] searchHits = hits.getHits();
        List<CoursePubIndexVo> indexVoList = new ArrayList<>();
        //遍历hit数组解析Json字符串
        for (SearchHit searchHit : searchHits) {
            String json = searchHit.getSourceAsString();
            //转成index实体对象
            CoursePubIndex coursePubIndex = JSON.parseObject(json, CoursePubIndex.class);
            //解析高亮字段
            Map<String, HighlightField> map = searchHit.getHighlightFields();
            //获取高亮信息
            if (ObjectUtils.isNotEmpty(map) && map.containsKey("name")) {
                String name = map.get("name").getFragments()[0].toString();
                coursePubIndex.setName(name);
            }
            //在转成CoursePubVo对象
            CoursePubIndexVo coursePubIndexVo = BeanHelper.copyProperties(coursePubIndex, CoursePubIndexVo.class);
            coursePubIndexVo.setIndexId(coursePubIndex.getId());
            indexVoList.add(coursePubIndexVo);
        }
        return new PageResponseResult<>(hits.getTotalHits().value, indexVoList);
    }

    /**
     * 根据id从Es索引库中查询
     *
     * @param id id
     * @return 结果
     */
    @Override
    public ResponseResult findCourseById(Long id) throws IOException, BusinessException {
        //构造查询请求
        GetRequest request = new GetRequest("course", id.toString());
        //发送请求
        GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT);
        //解析请求
        //转为index实体对象
        CoursePubIndex coursePubIndex = JSON.parseObject(response.getSourceAsString(), CoursePubIndex.class);
        //判空
        if (ObjectUtils.isEmpty(coursePubIndex)) {
            throw new BusinessException(ErrorCode.NOTFOUND);
        }
        //转为Vo
        CoursePubIndexVo coursePubIndexVo = BeanHelper.copyProperties(coursePubIndex, CoursePubIndexVo.class);
        //设置id
        coursePubIndexVo.setIndexId(coursePubIndex.getId());
        return ResponseResult.okResult(coursePubIndexVo);
    }

    /**
     * 根据id从Es索引库中删除
     *
     * @param coursePubId id
     */
    @Override
    public void deleteCourse(Long coursePubId) throws IOException {
        //构建删除请求
        DeleteRequest request = new DeleteRequest("course", coursePubId.toString());
        //发送请求
        restHighLevelClient.delete(request, RequestOptions.DEFAULT);
    }
}
